package fr.umlv.m2.jee.spij.service.partial.impl;

import fr.umlv.m2.jee.spij.persistence.actor.StudentPartial;
import fr.umlv.m2.jee.spij.persistence.actor.TeacherPartial;
import fr.umlv.m2.jee.spij.persistence.actor.dao.HibernateStudentPartialDAO;
import fr.umlv.m2.jee.spij.persistence.actor.dao.HibernateTeacherPartialDAO;
import fr.umlv.m2.jee.spij.persistence.education.Partial;
import fr.umlv.m2.jee.spij.persistence.education.Unit;
import fr.umlv.m2.jee.spij.persistence.education.dao.HibernatePartialDAO;
import fr.umlv.m2.jee.spij.persistence.education.dao.HibernateUnitDAO;
import fr.umlv.m2.jee.spij.service.ldap.authentification.DisplayActor;
import fr.umlv.m2.jee.spij.service.partial.DisplayPartial;
import fr.umlv.m2.jee.spij.service.partial.IDisplayPartialService;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Default service about partials
 *
 * @author thomas magny
 * @author mathieu mazzolini
 */
@Service("defaultDisplayPartialService")
public class DefaultDisplayPartialService implements IDisplayPartialService {

    @Autowired
    private HibernatePartialDAO partialDAO;

    @Autowired
    private HibernateStudentPartialDAO studentPartialDAO;

    @Autowired
    private HibernateTeacherPartialDAO teacherPartialDAO;

    @Autowired
    private HibernateUnitDAO unitDAO;

    DefaultDisplayPartialService() {
    }

    /*
     * (non-Javadoc)
     * 
     * @see fr.umlv.m2.jee.spij.service.partial.IDisplayPartialService#
     * getAllDisplayPartial(long,
     * org.springframework.binding.message.MessageContext)
     */

    @Override
    @Transactional(readOnly = true)
    public List<DisplayPartial> getAllDisplayPartial(long unitId, MessageContext context) {
        final Unit unit = unitDAO.findById(unitId);
        final List<Partial> partials = unit.getPartials();
        final List<DisplayPartial> displayPartials = new ArrayList<DisplayPartial>();

        if (partials == null) {
            context.addMessage(new MessageBuilder().error().code("partials.empty").build());
        } else {
            for (Partial partial : partials) {
                DisplayPartial displayPartial = new DisplayPartial(partial);
                if (unit != null)
                    displayPartial.setUnitName(unit.getName());
                displayPartials.add(displayPartial);
            }
        }
        return displayPartials;
    }

    /*
     * (non-Javadoc)
     * 
     * @see fr.umlv.m2.jee.spij.service.partial.IDisplayPartialService#
     * getAllDisplayPartialPerUser(long,
     * fr.umlv.m2.jee.spij.service.ldap.authentification.DisplayActor,
     * org.springframework.binding.message.MessageContext)
     */

    @Override
    @Transactional(readOnly = true)
    public List<DisplayPartial> getAllDisplayPartialPerUser(final long unitId, DisplayActor currentUser, MessageContext context) {
        if (currentUser.isTeacher() || currentUser.isStudent()) {
            final Unit unit = unitDAO.findById(unitId);
            final List<Partial> Partials = partialDAO.findByLdapActorAndUnitId(unitId, currentUser.getLogin(), currentUser.getType());
            final List<DisplayPartial> displayPartials = new ArrayList<DisplayPartial>(Partials.size());

            if (Partials.isEmpty()) {
                context.addMessage(new MessageBuilder().error().code("partials.empty").build());
            } else {
                for (Partial partial : Partials) {
                    DisplayPartial displayPartial = new DisplayPartial(partial);
                    if (unit != null)
                        displayPartial.setUnitName(unit.getName());
                    displayPartials.add(displayPartial);
                }
            }
            return displayPartials;
        }

        return getAllDisplayPartial(unitId, context);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * fr.umlv.m2.jee.spij.service.partial.IDisplayPartialService#getPartialById
     * (long, org.springframework.binding.message.MessageContext)
     */

    @Override
    @Transactional(readOnly = true)
    public DisplayPartial getPartialById(long id, MessageContext context) {
        final Partial partial = partialDAO.findById(id);
        final DisplayPartial displayPartial = new DisplayPartial(partial);

        if (partial == null) {
            context.addMessage(new MessageBuilder().error().code("partial.empty").build());
        } else {
            if (partial.getUnit() != null) {
                displayPartial.setUnitName(unitDAO.findById(partial.getUnit()).getName());
            }
        }
        return displayPartial;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * fr.umlv.m2.jee.spij.service.partial.IDisplayPartialService#createPartial
     * (fr.umlv.m2.jee.spij.service.partial.DisplayPartial, long,
     * org.springframework.binding.message.MessageContext)
     */

    @Override
    @Transactional(readOnly = false)
    public boolean createPartial(DisplayPartial displayPartial, long unitId, MessageContext context) {
        Partial partial = new Partial();
        partial.setUnit(unitId);
        partial.setSubject(displayPartial.getSubject());
        partial.setCorrection(displayPartial.getCorrection());
        partial.setLinkCorrection(displayPartial.getLinkCorrection());

        if (displayPartial.getWeight() <= 0) {
            context.addMessage(new MessageBuilder().error().code("partial.weight.positive").build());
            return false;
        }
        partial.setWeight(displayPartial.getWeight());

        Date date = displayPartial.getDate();
        DateTime startTime = new DateTime(date.getTime());
        startTime = startTime.plus(displayPartial.getStartTime().getTime());
        DateTime endTime = new DateTime(date.getTime());
        endTime = endTime.plus(displayPartial.getEndTime().getTime());

        if (startTime.isAfter(endTime)) {
            context.addMessage(new MessageBuilder().error().code("partial.startTime.isAfter.endTime").build());
            return false;
        }

        final Interval duration = new Interval(startTime, endTime);
        partial.setDuration(duration);

        final Date consultationDate = displayPartial.getConsultationDate();
        final Date consultationHour = displayPartial.getConsultationHour();
        if (consultationDate != null && consultationHour != null) {
            DateTime consultation = new DateTime(consultationDate.getTime());
            consultation = consultation.plus(consultationHour.getTime());
            if (endTime.isAfter(consultation)) {
                context.addMessage(new MessageBuilder().error().code("partial.consultation.isBefore.endTime").build());
                return false;
            }
            partial.setConsultation(consultation);
        } else if (consultationDate != null) {
            context.addMessage(new MessageBuilder().error().code("partial.consultation.incomplete").build());
            return false;
        } else if (consultationHour != null) {
            context.addMessage(new MessageBuilder().error().code("partial.consultation.incomplete").build());
            return false;
        }

        partialDAO.persist(partial);
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * fr.umlv.m2.jee.spij.service.partial.IDisplayPartialService#updatePartialById
     * (fr.umlv.m2.jee.spij.service.partial.DisplayPartial,
     * org.springframework.binding.message.MessageContext)
     */

    @Transactional(readOnly = false)
    @Override
    public boolean updatePartialById(DisplayPartial modifiedPartial, MessageContext context) {
        Partial partial = partialDAO.findById(modifiedPartial.getId());
        boolean hasBeenModified = false;

        if (partial != null) {
            if (!partial.getSubject().equals(modifiedPartial.getSubject())) {
                hasBeenModified = true;
                partial.setSubject(modifiedPartial.getSubject());
            }
            if (!partial.getCorrection().equals(modifiedPartial.getCorrection())) {
                hasBeenModified = true;
                partial.setCorrection(modifiedPartial.getCorrection());
            }
            if (!partial.getLinkCorrection().equals(modifiedPartial.getLinkCorrection())) {
                hasBeenModified = true;
                partial.setLinkCorrection(modifiedPartial.getLinkCorrection());
            }
            if (partial.getWeight() != modifiedPartial.getWeight()) {
                if (modifiedPartial.getWeight() <= 0) {
                    context.addMessage(new MessageBuilder().error().code("partial.weight.positive").build());
                    return false;
                }
                hasBeenModified = true;
                partial.setWeight(modifiedPartial.getWeight());
            }

            //Duration update
            Date date = modifiedPartial.getDate();
            DateTime startTime = new DateTime(date.getTime());
            startTime = startTime.plus(modifiedPartial.getStartTime().getTime());
            DateTime endTime = new DateTime(date.getTime());
            endTime = endTime.plus(modifiedPartial.getEndTime().getTime());

            DateTime previousStartDate = partial.getDuration().getStart();
            DateTime previousEndDate = partial.getDuration().getEnd();

            if (startTime.isAfter(endTime)) {
                context.addMessage(new MessageBuilder().error().code("partial.startTime.isAfter.endTime").build());
                return false;
            }

            //Consultation update
            final Date consultationDate = modifiedPartial.getConsultationDate();
            final Date consultationHour = modifiedPartial.getConsultationHour();

            DateTime previousConsultation = partial.getConsultation();

            if (consultationDate != null && consultationHour != null) {
                DateTime consultation = new DateTime(consultationDate.getTime());
                consultation = consultation.plus(consultationHour.getTime());
                if (endTime.isAfter(consultation)) {
                    context.addMessage(new MessageBuilder().error().code("partial.consultation.isBefore.endTime").build());
                    return false;
                }
                if (!consultation.equals(previousConsultation)) {
                    hasBeenModified = true;
                    partial.setConsultation(consultation);
                }
            } else if (consultationDate != null) {
                context.addMessage(new MessageBuilder().error().code("partial.consultation.incomplete").build());
                return false;
            } else if (consultationHour != null) {
                context.addMessage(new MessageBuilder().error().code("partial.consultation.incomplete").build());
                return false;
            } else {
                if (previousConsultation != null) {
                    hasBeenModified = true;
                    partial.setConsultation(null);
                }
            }

            if (!previousStartDate.equals(startTime) || !previousEndDate.equals(endTime)) {
                hasBeenModified = true;
                partial.setDuration(new Interval(startTime, endTime));
            }

            partialDAO.merge(partial);
        }

        if (!hasBeenModified) {
            context.addMessage(new MessageBuilder().error().code("updatePartialFail").build());
        }

        return hasBeenModified;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * fr.umlv.m2.jee.spij.service.partial.IDisplayPartialService#deletePartialById
     * (long, org.springframework.binding.message.MessageContext)
     */

    @Transactional(readOnly = false)
    @Override
    public boolean deletePartialById(long id, MessageContext context) {
        Partial partial = partialDAO.findById(id);
        List<StudentPartial> students = partial.getStudents();
        for (StudentPartial studentPartial : students) {
            studentPartialDAO.remove(studentPartial);
            studentPartialDAO.flush();
        }
        List<TeacherPartial> teachers = partial.getTeachers();
        for (TeacherPartial teacherPartial : teachers) {
            teacherPartialDAO.remove(teacherPartial);
            teacherPartialDAO.flush();
        }
        partialDAO.remove(partial);
        partialDAO.flush();
        return true;
    }
}
